home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright 1991, 1992, 1993, 1994, Silicon Graphics, Inc.
- * All Rights Reserved.
- *
- * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
- * the contents of this file may not be disclosed to third parties, copied or
- * duplicated in any form, in whole or in part, without the prior written
- * permission of Silicon Graphics, Inc.
- *
- * RESTRICTED RIGHTS LEGEND:
- * Use, duplication or disclosure by the Government is subject to restrictions
- * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
- * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
- * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
- * rights reserved under the Copyright Laws of the United States.
- */
- /*
- * quant -
- * general median cut algorithm follows. This is
- * based on heckbert's median cut algoirthm.
- *
- * Paul Haeberli - 1990
- */
- #include "stdio.h"
- #include "quant.h"
-
- #define MAXCOLORS (256)
- #define NINCHUNK (40)
- #define SPACEDIM (64)
- #define NOCOLOR (10000)
-
- #define RDIR (1)
- #define GDIR (2)
- #define BDIR (3)
-
- typedef struct colorvol {
- struct colorvol *next;
- long rmin, rmax, rcut, rdev;
- long gmin, gmax, gcut, gdev;
- long bmin, bmax, bcut, bdev;
- int avgdev;
- int npixels;
- int splitdir;
- int number;
- } colorvol;
-
- static long carray[SPACEDIM][SPACEDIM][SPACEDIM];
- static short iarray[SPACEDIM][SPACEDIM][SPACEDIM];
- static long ctab[MAXCOLORS][3];
- static short rbuf[MAXCOLORS];
- static short gbuf[MAXCOLORS];
- static short bbuf[MAXCOLORS];
- static colorvol *cvarray[MAXCOLORS];
- static colorvol *fvols = 0;
- static colorvol *root = 0;
- static int ncolors;
- static int nnodes;
- static int heckmode;
-
- static clearspace()
- {
- long i;
- long *lptr;
-
- lptr = carray[0][0];
- for(i=SPACEDIM*SPACEDIM*SPACEDIM; i--;)
- *lptr++ = 0;
- }
-
- static navgtospace(r, g, b)
- int r, g, b;
- {
- int index, n;
-
- index = iarray[r][g][b];
- if(index == NOCOLOR)
- return;
- n = carray[r][g][b];
- if(n) {
- r = (255*r)/(SPACEDIM-1);
- g = (255*g)/(SPACEDIM-1);
- b = (255*b)/(SPACEDIM-1);
- ctab[index][0] += n*r;
- ctab[index][1] += n*g;
- ctab[index][2] += n*b;
- }
- }
-
- static freevol( cv )
- colorvol *cv;
- {
- if( cv ) {
- cv->next = fvols;
- fvols = cv;
- }
- }
-
- static colorvol *newvol()
- {
- colorvol *cv;
- int i;
-
- if(!fvols) {
- cv = (colorvol *)mymalloc(NINCHUNK*sizeof(colorvol));
- for(i=0; i<NINCHUNK; i++)
- freevol(cv++);
- }
- cv = fvols;
- fvols = fvols->next;
- return cv;
- }
-
- static zaphist(hist,min,max)
- long hist[];
- long min, max;
- {
- int i;
-
- for(i=min; i<=max; i++)
- hist[i] = 0;
- }
-
- static rangehist(hist,min,max,npix,ravg,rdev)
- long hist[];
- long min, max, npix, *ravg, *rdev;
- {
- int i;
- long delta, avg, dev;
-
- avg = 0;
- for(i=min; i<=max; i++) {
- avg += i*hist[i];
- }
- avg = avg/npix;
-
- dev = 0;
- for(i=min; i<=max; i++) {
- delta = i-avg;
- dev += hist[i]*delta*delta;
- }
- *ravg = avg;
- *rdev = dev>>8;
-
- if(avg == min)
- return 0;
- else
- return 1;
- }
-
- #define FLAGMAX (12000)
-
- static makenode(rmin,rmax,gmin,gmax,bmin,bmax)
- int rmin, rmax, gmin, gmax, bmin, bmax;
- {
- colorvol *cv, *ptr;
- int Rmin, Rmax, Gmin, Gmax, Bmin, Bmax;
- int r, g, b;
- int rok, gok, bok;
- long npixels, val;
- long rhist[SPACEDIM];
- long ghist[SPACEDIM];
- long bhist[SPACEDIM];
-
- cv = newvol();
-
- Rmin = Gmin = Bmin = FLAGMAX;
- Rmax = Gmax = Bmax = -FLAGMAX;
-
- zaphist(rhist,rmin,rmax);
- zaphist(ghist,gmin,gmax);
- zaphist(bhist,bmin,bmax);
- npixels = 0;
- for(b=bmin; b<=bmax; b++) {
- for(g=gmin; g<=gmax; g++) {
- for(r=rmin; r<=rmax; r++) {
- val = carray[r][g][b];
- if(val) {
- if(r<Rmin)
- Rmin = r;
- if(g<Gmin)
- Gmin = g;
- if(b<Bmin)
- Bmin = b;
-
- if(r>Rmax)
- Rmax = r;
- if(g>Gmax)
- Gmax = g;
- if(b>Bmax)
- Bmax = b;
- npixels+=val;
- rhist[r] += val;
- ghist[g] += val;
- bhist[b] += val;
- }
- }
- }
- }
- if(npixels == 0)
- return;
-
- cv->npixels = npixels;
- cv->rmin = Rmin;
- cv->gmin = Gmin;
- cv->bmin = Bmin;
- cv->rmax = Rmax;
- cv->gmax = Gmax;
- cv->bmax = Bmax;
- rok = rangehist(rhist,cv->rmin,cv->rmax,npixels,&cv->rcut,&cv->rdev);
- gok = rangehist(ghist,cv->gmin,cv->gmax,npixels,&cv->gcut,&cv->gdev);
- bok = rangehist(bhist,cv->bmin,cv->bmax,npixels,&cv->bcut,&cv->bdev);
-
- if(rok && cv->rdev>=cv->gdev && cv->rdev>=cv->bdev)
- cv->splitdir = RDIR;
- else if(gok && cv->gdev>=cv->rdev && cv->gdev>=cv->bdev)
- cv->splitdir = GDIR;
- else if(bok)
- cv->splitdir = BDIR;
- else
- cv->splitdir = 0;
-
- cv->avgdev = cv->rdev+cv->gdev+cv->bdev;
- #ifdef OLDWAY
- cv->avgdev = ILUM(cv->rdev,cv->gdev,cv->bdev);
- #endif
- cv->next = root;
- root = cv;
- nnodes++;
- }
-
- static avgtospace(short *r, short *g, short *b, int n)
- {
- int tr, tg, tb;
- int ir, ig, ib;
- int index;
-
- while(n--) {
- tr = *r++;
- tg = *g++;
- tb = *b++;
- ir = (SPACEDIM*tr)/256;
- ig = (SPACEDIM*tg)/256;
- ib = (SPACEDIM*tb)/256;
- index = iarray[ir][ig][ib];
- if(index == NOCOLOR) {
- fprintf(stderr,"hquant: bad poop\n");
- exit(1);
- } else {
- ctab[index][0] += tr;
- ctab[index][1] += tg;
- ctab[index][2] += tb;
- }
- }
- }
-
- static divspace(cv)
- colorvol *cv;
- {
- switch(cv->splitdir) {
- case RDIR:
- makenode(cv->rmin,cv->rcut-1,cv->gmin,cv->gmax,cv->bmin,cv->bmax);
- makenode(cv->rcut,cv->rmax, cv->gmin,cv->gmax,cv->bmin,cv->bmax);
- break;
- case GDIR:
- makenode(cv->rmin,cv->rmax,cv->gmin,cv->gcut-1,cv->bmin,cv->bmax);
- makenode(cv->rmin,cv->rmax,cv->gcut,cv->gmax, cv->bmin,cv->bmax);
- break;
- case BDIR:
- makenode(cv->rmin,cv->rmax,cv->gmin,cv->gmax,cv->bmin,cv->bcut-1);
- makenode(cv->rmin,cv->rmax,cv->gmin,cv->gmax,cv->bcut,cv->bmax );
- break;
- default:
- fprintf(stderr,"quant: divspace: bad poop %d\n",cv->splitdir);
- exit(1);
- }
- nnodes--;
- cv->npixels = 0;
- cv->avgdev = 0;
- }
-
- /*
- * gammawarp stuff follows
- *
- *
- */
- static float curgamma;
- static short gamtab[256];
-
- static gammawarp(sbuf,gam,n)
- short *sbuf;
- float gam;
- int n;
- {
- int i;
- float f;
-
- if(gam!=curgamma) {
- for(i=0; i<256; i++)
- gamtab[i] = 255*pow(i/255.0,gam)+0.5;
- curgamma = gam;
- }
- while(n--) {
- *sbuf = gamtab[*sbuf];
- sbuf++;
- }
- }
-
- static lumwarp(r,g,b,gamma)
- short *r, *g, *b;
- float gamma;
- {
- int max, nmax;
-
- max = *r;
- if(*g>max)
- max = *g;
- if(*b>max)
- max = *b;
- if(max>0) {
- nmax = 255*pow(max/255.0,gamma)+0.49;
- *r = (nmax * *r)/max;
- *g = (nmax * *g)/max;
- *b = (nmax * *b)/max;
- } else {
- *r = 0;
- *g = 0;
- *b = 0;
- }
- }
-
- void optmapbegin(int nc, int mode)
- {
- ncolors = nc;
- if(ncolors>MAXCOLORS) {
- fprintf(stderr,"quant: num colors exceeds %d\n",MAXCOLORS);
- exit(1);
- }
- heckmode = mode;
- clearspace();
- }
-
- void optmapadd( short *r, short *g, short *b, int n)
- {
- int ir, ig, ib;
-
- while(n--) {
- ir = (SPACEDIM*(*r++))/256;
- ig = (SPACEDIM*(*g++))/256;
- ib = (SPACEDIM*(*b++))/256;
- carray[ir][ig][ib]++;
- }
- }
-
- cmap *optmapend( void )
- {
- int i, r, g, b;
- int totcolors, bigest;
- colorvol *bigcv, *cv;
-
- /* repeat division of the smallest piece for ncolors */
- root = NULL;
- nnodes = 0;
- makenode(0,SPACEDIM-1,0,SPACEDIM-1,0,SPACEDIM-1);
-
- if(heckmode) {
- while(nnodes<ncolors) {
- bigcv = 0;
- bigest = 0;
- cv = root;
- while(cv) {
- if(cv->splitdir && cv->npixels>bigest) {
- bigcv = cv;
- bigest = cv->npixels;
- }
- cv = cv->next;
- }
- if(bigcv)
- divspace(bigcv);
- else {
- fprintf(stderr,"N colors reduced to %d from %d\n",nnodes,ncolors);
- ncolors = nnodes;
- break;
- }
- }
- } else {
- while(nnodes<ncolors) {
- bigcv = 0;
- bigest = 0;
- cv = root;
- while(cv) {
- if(cv->splitdir && cv->avgdev>bigest) {
- bigcv = cv;
- bigest = cv->avgdev;
- }
- cv = cv->next;
- }
- if(bigcv)
- divspace(bigcv);
- else {
- fprintf(stderr,"Ncolor reduced to %d from %d\n",nnodes,ncolors);
- ncolors = nnodes;
- break;
- }
- }
- }
-
- /* clear the iarray to NOCOLOR */
- for(r=0; r<SPACEDIM; r++)
- for(g=0; g<SPACEDIM; g++)
- for(b=0; b<SPACEDIM; b++)
- iarray[r][g][b] = NOCOLOR;
-
- /* set the color numbers for all the volumes */
- cv=root;
- totcolors = 0;
- while(cv) {
- if(cv->npixels) {
- cvarray[totcolors] = cv;
- cv->number = totcolors;
- for(r=cv->rmin; r<=cv->rmax; r++)
- for(g=cv->gmin; g<=cv->gmax; g++)
- for(b=cv->bmin; b<=cv->bmax; b++)
- iarray[r][g][b] = totcolors;
- totcolors++;
- }
- cv = cv->next;
- }
-
- /* read the input image again, finding average colors */
-
- for(i=0; i<ncolors; i++) {
- ctab[i][0] = 0;
- ctab[i][1] = 0;
- ctab[i][2] = 0;
- }
- for(b=0; b<SPACEDIM; b++) {
- for(g=0; g<SPACEDIM; g++) {
- for(r=0; r<SPACEDIM; r++) {
- navgtospace(r,g,b);
- }
- }
- }
- for(i=0; i<ncolors; i++) {
- ctab[i][0] /= cvarray[i]->npixels;
- ctab[i][1] /= cvarray[i]->npixels;
- ctab[i][2] /= cvarray[i]->npixels;
- rbuf[i] = ctab[i][0];
- gbuf[i] = ctab[i][1];
- bbuf[i] = ctab[i][2];
- }
- return newcmap(rbuf,gbuf,bbuf,0,ncolors);
- }
-